package com.xiaoming;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

@Component
@RefreshScope
public class IPFilter implements Ordered, GlobalFilter, ApplicationListener<ContextRefreshedEvent> {
    private static Map<String, List<Long>> map = new HashMap<>();

    @Value("${fileter.ip.duration}")
    private int duration;
    @Value("${fileter.ip.times}")
    private int times;

    private ScheduledExecutorService scheduledExecutorService = null;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        System.out.println("次数：" + times + " 时间间隔：" + duration);
        String host = request.getRemoteAddress().getHostString();
        MultiValueMap<String, HttpCookie> cookies = request.getCookies();
        String path = request.getPath().toString();
        boolean b = path.contains("/login") ||
                path.contains("/register") || path.contains("/code")
                || path.contains("/mail")|| path.contains("/isRegistered");
        if (!cookies.containsKey("sc-token") && !b) {
            String url = "http://www.test.com/static/login.html";
            response.setStatusCode(HttpStatus.SEE_OTHER);
            response.getHeaders().set(HttpHeaders.LOCATION, url);
            return response.setComplete();
        }
        long now = System.currentTimeMillis();
        Long expireTime = now + duration;
        if (!map.containsKey(host)) {
            List<Long> arrs = Collections.synchronizedList(new ArrayList<>());
            arrs.add(expireTime);
            map.put(host, arrs);
        } else {
            List<Long> list = map.get(host);
            list.add(expireTime);
            List<Long> arrs = new ArrayList<>();
            for (Long time : list) {
                if (now < time) {
                    arrs.add(time);
                }
            }
            System.out.println(arrs.size());
            if (arrs.size() > times) {
                response.setStatusCode(HttpStatus.FORBIDDEN);
                String data = "提交次数已达上限，请稍后重试！";
                DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
                return response.writeWith(Mono.just(wrap));
            }
        }
        return chain.filter(exchange);
    }

    private void refreshList() {
        if (scheduledExecutorService != null) {
            return;
        }
        scheduledExecutorService = Executors.newScheduledThreadPool(1);
        AtomicLong time = new AtomicLong(System.currentTimeMillis());
        scheduledExecutorService.scheduleWithFixedDelay(() -> {
            Set<Map.Entry<String, List<Long>>> entries = map.entrySet();
            System.out.println("执行清空操作----" + (System.currentTimeMillis() - time.get()));
            time.set(System.currentTimeMillis());
            for (Map.Entry<String, List<Long>> entry : entries) {
                List<Long> list = map.get(entry.getKey());
                Iterator<Long> iterator = list.iterator();
                while (iterator.hasNext()) {
                    Long next = iterator.next();
                    if (next < System.currentTimeMillis()) {
                        iterator.remove();
                    }
                }
            }
        }, 0, duration, TimeUnit.MILLISECONDS);
    }

    @Override
    public int getOrder() {
        return 0;
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        refreshList();
    }
}
